package main

import "fmt"

// dump slice length, capacity, and contents
func dump(label string, slice []string) {
	fmt.Printf("%v: length %v, capacity %v %v\n",
		label, len(slice), cap(slice), slice)
}

// 长度和容量
// Slice有两个比较混淆的概念，就是长度和容量，
// 何谓长度？跟数组的长度是一个概念，即在内存中进行了初始化实际存在的元素的个数。
// 何谓容量？如果通过make函数创建Slice的时候指定了容量参数，
// 那内存管理器会根据指定的容量的值先划分一块内存空间，然后才在其中存放有数组元素，
// 多余部分处于空闲状态，
// 在Slice上追加元素的时候，首先会放到这块空闲的内存中，如果添加的参数个数超过了容量值，
// 内存管理器会重新划分一块容量值为原容量值*2大小的内存空间，依次类推。
// 这个机制的好处在能够提升运算性能，因为内存的重新划分会降低性能。

// 有点像java 的list扩容
func main() {
	// 数组的len和cap是永远相等的，并且是在定义的时候就已经指定的，不能改变。
	a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	fmt.Println(a, len(a), cap(a)) //[1 2 3 4 5 6 7 8 9 0] 10 10

	// 切片s引用这个数组的全部元素，初始长度和容量都为10，
	// 继续追加3个元素后，其长度变为13容量为扩大到2倍变为20。
	s := a[0:]
	s = append(s, 11, 22, 33)
	fmt.Println(s, len(s), cap(s)) //[1 2 3 4 5 6 7 8 9 0 11 22 33] 13 20

	// 切片sa截取下标2到7的数组片段，长度为5，容量为8，
	// `这个容量的改变规则为原容量值减掉起始下标，8=10-2`  这个规则要记住
	sa := a[2:7]
	fmt.Println(sa, len(sa), cap(sa)) //[3 4 5 6 7] 5 8
	fmt.Println(a, len(a), cap(a))    //[1 2 3 4 5 6 7 8 9 0] 10 10

	// 此时若追加元素，会覆盖掉原内存地址中存在的值。本来7后面是8 但是现在变成了2
	sa = append(sa, 2)
	fmt.Println(sa, len(sa), cap(sa)) //[3 4 5 6 7 2] 6 8
	fmt.Println(a, len(a), cap(a))    //[1 2 3 4 5 6 7 2 9 0] 10 10

	// 切片sb截取切片sa下标3到5的数组片段，
	// 注意，这里的下标指的是sa的下标，不是源数组的下标，长度为2，容量为8-3=5。
	sb := sa[3:5]
	fmt.Println(sb, len(sb), cap(sb)) //[6 7] 2 5

}
